# -*- coding: utf-8 -*-
# @File: backend => room_view.py
# @CreateTime: 2025/8/26 20:30
# @Author: EvanZhang
# @Info:
from datetime import datetime, UTC

from fastapi import Depends, HTTPException
from fastapi.responses import JSONResponse
from typing import List
import redis.asyncio as pyredis
import json
import logging

from apps.models import Room, RoomPlayer, User
from apps.settings.redis import init_redis_pool_without_depend
# 导入房间相关WebSocket管理器实例
from apps.api.websocket_api import room_manager as manager

# 配置日志记录器
logger = logging.getLogger(__name__)


# 房间广播函数
async def broadcast_to_room(room_id: int, event_type: str, data: dict):
    """
    向指定房间的所有连接用户广播消息
    
    Args:
        room_id: 房间ID - 房间唯一标识
        event_type: 事件类型 - 如 'player_join', 'player_leave', 'game_start' 等
        data: 广播数据 - 包含事件相关的详细信息
    
    Returns:
        None
    
    Note:
        当前实现向所有连接的客户端广播消息
        未来可以优化为只向特定房间的用户广播
    """
    try:
        # 构造标准化的WebSocket消息格式
        message = {
            "type": event_type,
            "room_id": room_id,
            "data": data,
            "timestamp": datetime.now(UTC).isoformat()
        }
        
        # 使用房间管理器广播消息
        await manager.broadcast(message)
        
        # 记录广播日志（可选）
        logger.info(f"房间 {room_id} 广播事件: {event_type}")
        
    except Exception as e:
        # 广播失败时记录错误，但不影响主业务流程
         logger.error(f"房间 {room_id} 广播失败: {str(e)}")


async def broadcast_room_status(room_id: int, status: str, additional_data: dict = None):
    """
    广播房间状态变化
    
    Args:
        room_id: 房间ID
        status: 房间状态 - 如 'waiting', 'playing', 'finished'
        additional_data: 额外数据
    """
    data = {
        "room_id": room_id,
        "status": status,
        "message": f"房间 {room_id} 状态变更为: {status}"
    }
    
    if additional_data:
        data.update(additional_data)
    
    await broadcast_to_room(room_id, "room_status_change", data)


async def broadcast_game_event(room_id: int, event_type: str, event_data: dict):
    """
    广播游戏事件
    
    Args:
        room_id: 房间ID
        event_type: 游戏事件类型 - 如 'game_start', 'new_hand', 'betting_round', 'showdown'
        event_data: 事件数据
    """
    await broadcast_to_room(room_id, f"game_{event_type}", {
        "room_id": room_id,
        "event": event_type,
        **event_data
    })

# 创建房间
async def create_room(room):
    try:
        # 检查房间名是否已存在
        existing_room = await Room.filter(name=room.name).first()
        if existing_room:
            return 400, "房间名已存在，请选择其他名称"
        # 创建房间
        room = await Room.create(**room.model_dump())
        room_id = room.id

        # 初始化Redis
        # redis = await init_redis_pool_without_depend()
        async with init_redis_pool_without_depend() as redis:
            await redis.hset(f'room:{room_id}:info', mapping={
                'sb':10,
                'bb':20,
                'dealer_pos':0,
                'current_hand_id':0,
                'status':'waiting'
            })
            seats = ['""'] * room.max_players  # 创建空座位列表
            await redis.lpush(f'room:{room_id}:seats', *seats)
        return 201, room
    except Exception as e:
        return 500, str(e)

# 加入房间
async def join_room(room_id: int, user_id: int, buy_in_amount: int):
    try:
        # 检查房间是否存在
        room = await Room.filter(id=room_id).first()
        if not room:
            return 404, "房间不存在"
        
        # 检查用户是否存在
        user = await User.filter(id=user_id).first()
        if not user:
            return 404, "用户不存在"
        
        # 检查用户筹码是否足够
        if user.chips < buy_in_amount:
            return 400, "筹码不足"
        
        # 检查用户是否已经在房间中（未离开的玩家）
        existing_active_player = await RoomPlayer.filter(room_id=room_id, user_id=user_id, left_at__isnull=True).first()
        if existing_active_player:
            return 400, "您已经在房间中"
        
        async with init_redis_pool_without_depend() as redis:
            # 获取所有座位状态
            seats = await redis.lrange(f'room:{room_id}:seats', 0, -1)
            
            # 找到空座位
            seat_index = None
            for i, seat in enumerate(seats):
                # Redis返回的是字节类型，需要解码后比较
                seat_str = seat.decode('utf-8') if isinstance(seat, bytes) else seat
                if seat_str == '' or seat_str == '""' or seat_str == 'null':
                    seat_index = i
                    break
            
            if seat_index is None:
                return 400, "房间已满"
            
            # 准备用户信息JSON
            user_info = {
                "user_id": user_id,
                "username": user.username,
                "avatar_url": user.avatar_url,
                "chips": buy_in_amount
            }
            user_info_json = json.dumps(user_info)
            
            # Redis操作
            # 设置座位信息
            await redis.lset(f'room:{room_id}:seats', seat_index, user_info_json)
            
            # 加入在线用户集合
            await redis.sadd('online_users', user_id)
            
            # 扣除用户筹码（原子操作）
            await redis.decrby(f'user:{user_id}:chips', buy_in_amount)
        
        # 更新数据库中的用户筹码
        new_user_chips = user.chips - buy_in_amount
        await User.filter(id=user_id).update(chips=new_user_chips)
        logger.info(f"用户 {user_id} 加入房间 {room_id}：筹码扣除 {buy_in_amount}，{user.chips} -> {new_user_chips}")
        
        # MySQL操作：检查是否存在历史记录
        existing_player = await RoomPlayer.filter(room_id=room_id, user_id=user_id).first()
        
        if existing_player:
            # 如果存在历史记录，更新记录（重新加入）
            await RoomPlayer.filter(id=existing_player.id).update(
                seat_index=seat_index,
                buy_in_chips=buy_in_amount,
                left_chips=buy_in_amount,
                created_at=datetime.now(UTC),  # 更新加入时间
                left_at=None  # 清空离开时间
            )
            room_player = existing_player
        else:
            # 如果不存在历史记录，创建新记录
            room_player = await RoomPlayer.create(
                room_id=room_id,
                user_id=user_id,
                seat_index=seat_index,
                buy_in_chips=buy_in_amount,
                left_chips=buy_in_amount  # 初始剩余筹码等于买入筹码
            )

        # 返回结果
        result_data = {
            "seat_index": seat_index,
            "buy_in_chips": buy_in_amount
        }
        
        # WebSocket广播 - 向房间广播玩家加入事件
        await broadcast_to_room(room_id, "player_join", {
            "user_id": user_id,
            "username": user.username,
            "seat_index": seat_index,
            "buy_in_chips": buy_in_amount,
            "avatar_url": user.avatar_url,
            "action": "join",
            "message": f"玩家 {user.username} 加入了房间，坐在 {seat_index + 1} 号位"
        })
        
        return 200, result_data
        
    except Exception as e:
        return 500, str(e)

# 获取房间快照
async def get_room_snapshot(room_id: int):
    try:
        # 检查房间是否存在
        room = await Room.filter(id=room_id).first()
        if not room:
            return 404, "房间不存在"
        
        async with init_redis_pool_without_depend() as redis:
            # 获取房间基本信息
            room_info = await redis.hmget(
                f'room:{room_id}:info',
                'sb', 'bb', 'dealer_pos', 'status', 'to_act_seat', 'action_deadline'
            )
            
            # 获取座位信息
            seats_data = await redis.lrange(f'room:{room_id}:seats', 0, -1)
            
            # 获取公共牌
            board_data = await redis.lrange(f'room:{room_id}:board', 0, -1)
            
            # 获取奖池信息
            pot_data = await redis.hgetall(f'room:{room_id}:pot')
        
        # 处理座位信息
        seats = []
        for seat in seats_data:
            seat_str = seat.decode('utf-8') if isinstance(seat, bytes) else seat
            if seat_str == '' or seat_str == '""' or seat_str == 'null':
                seats.append(None)  # 空座位
            else:
                try:
                    seat_info = json.loads(seat_str)
                    # 添加默认的游戏状态字段
                    seat_info.setdefault('state', 'active')
                    seat_info.setdefault('last_action', None)
                    seat_info.setdefault('chips_on_table', 0)
                    seats.append(seat_info)
                except json.JSONDecodeError:
                    seats.append(None)
        
        # 处理公共牌
        board = []
        for card in board_data:
            card_str = card.decode('utf-8') if isinstance(card, bytes) else card
            if card_str and card_str != '':
                board.append(card_str)
        
        # 处理奖池信息
        pots = {}
        for key, value in pot_data.items():
            key_str = key.decode('utf-8') if isinstance(key, bytes) else key
            value_str = value.decode('utf-8') if isinstance(value, bytes) else value
            try:
                pots[key_str] = int(value_str) if value_str.isdigit() else 0
            except (ValueError, AttributeError):
                pots[key_str] = 0
        
        # 处理Redis返回的基本信息
        sb = int(room_info[0].decode('utf-8')) if room_info[0] else room.sb
        bb = int(room_info[1].decode('utf-8')) if room_info[1] else room.bb
        dealer_pos = int(room_info[2].decode('utf-8')) if room_info[2] else 0
        status = room_info[3].decode('utf-8') if room_info[3] else room.status
        to_act_seat = int(room_info[4].decode('utf-8')) if room_info[4] and room_info[4] != b'null' else None
        action_deadline = int(room_info[5].decode('utf-8')) if room_info[5] and room_info[5] != b'null' else None
        
        # 组装返回数据
        snapshot_data = {
            "id": room.id,
            "name": room.name,
            "sb": sb,
            "bb": bb,
            "max_players": room.max_players,
            "status": status,
            "seats": seats,
            "board": board,
            "pots": pots,
            "to_act_seat": to_act_seat,
            "action_deadline": action_deadline
        }
        
        return 200, snapshot_data
        
    except Exception as e:
        return 500, str(e)

# # 退出房间
# async def exit_room(room_id: int, user_id: int):
#     try:
#         # 检查房间是否存在
#         room = await Room.filter(id=room_id).first()
#         if not room:
#             return 404, "房间不存在"

#         # 检查用户是否存在
#         user = await User.filter(id=user_id).first()
#         if not user:
#             return 404, "用户不存在"

#         # 检查用户是否在房间中
#         room_player = await RoomPlayer.filter(room_id=room_id, user_id=user_id, left_at__isnull=True).first()
#         if not room_player:
#             return 400, "您不在该房间中"

#         seat_index = room_player.seat_index
#         left_chips = room_player.left_chips

#         async with init_redis_pool_without_depend() as redis:
#             # 获取座位信息
#             seat_data = await redis.lindex(f'room:{room_id}:seats', seat_index)
#             if not seat_data:
#                 return 400, "座位信息异常"

#             seat_str = seat_data.decode('utf-8') if isinstance(seat_data, bytes) else seat_data
#             if seat_str == '' or seat_str == '""' or seat_str == 'null':
#                 return 400, "座位已空"

#             try:
#                 seat_info = json.loads(seat_str)
#                 chips_on_table = seat_info.get('chips', 0)
#             except json.JSONDecodeError:
#                 return 400, "座位信息解析异常"

#             # 将筹码返还给用户（原子操作）
#             total_return_chips = left_chips + chips_on_table
#             await redis.incrby(f'user:{user_id}:chips', total_return_chips)

#             # 清空座位信息
#             await redis.lset(f'room:{room_id}:seats', seat_index, '""')

#             # 从在线用户集合移除
#             await redis.srem('online_users', user_id)

#         # 更新数据库中的用户筹码
#         await User.filter(id=user_id).update(chips=user.chips + total_return_chips)

#         # 更新房间玩家记录，设置离开时间和剩余筹码
#         await RoomPlayer.filter(id=room_player.id).update(left_at=datetime.now(UTC), left_chips=0)

#         # 返回结果
#         result_data = {
#             "id": room.id,
#             "name": room.name,
#             "sb": room.sb,
#             "bb": room.bb,
#             "max_players": room.max_players,
#             "status": room.status
#         }
        
#         # WebSocket广播 - 向房间广播玩家退出事件
#         await broadcast_to_room(room_id, "player_leave", {
#             "user_id": user_id,
#             "username": user.username,
#             "seat_index": seat_index,
#             "returned_chips": total_return_chips,
#             "action": "exit",
#             "message": f"玩家 {user.username} 退出了房间，带走了 {total_return_chips} 筹码",
#             "leave_type": "exit"  # 区分退出类型：exit(正常退出) vs leave(简单离开)
#         })
        
#         return 200, result_data
    
#     except Exception as e:
#         return 500, f"退出房间失败: {str(e)}"


async def leave_room(room_id: int, user_id: int):
    """离开房间 - 简化版本，只返回成功消息"""
    try:
        # 检查房间是否存在
        room = await Room.filter(id=room_id).first()
        if not room:
            return 404, "房间不存在"

        # 检查用户是否存在
        user = await User.filter(id=user_id).first()
        if not user:
            return 404, "用户不存在"

        # 检查用户是否在房间中
        room_player = await RoomPlayer.filter(room_id=room_id, user_id=user_id, left_at__isnull=True).first()
        if not room_player:
            return 400, "您不在该房间中"

        seat_index = room_player.seat_index
        left_chips = room_player.left_chips

        async with init_redis_pool_without_depend() as redis:
            # 获取座位信息
            seat_data = await redis.lindex(f'room:{room_id}:seats', seat_index)
            if not seat_data:
                return 400, "座位信息异常"

            seat_str = seat_data.decode('utf-8') if isinstance(seat_data, bytes) else seat_data
            if seat_str == '' or seat_str == '""' or seat_str == 'null':
                return 400, "座位已空"

            try:
                seat_info = json.loads(seat_str)
                chips_on_table = seat_info.get('chips', 0)
            except json.JSONDecodeError:
                return 400, "座位信息解析异常"

            # Redis操作：把该座位置空
            await redis.lset(f'room:{room_id}:seats', seat_index, '')
            
            # 从在线用户中移除
            await redis.srem('online_users', user_id)
            
            # 把剩余筹码加回用户账户
            logger.info(f"用户 {user_id} 离开房间 {room_id}：剩余筹码={left_chips}")
            await redis.incrby(f'user:{user_id}:chips', left_chips)

        # 更新数据库中的用户筹码
        new_user_chips = user.chips + left_chips
        await User.filter(id=user_id).update(chips=new_user_chips)
        logger.info(f"用户 {user_id} 筹码更新：{user.chips} -> {new_user_chips}")

        # MySQL：更新房间玩家记录
        await RoomPlayer.filter(id=room_player.id).update(
            left_at=datetime.now(UTC), 
            left_chips=left_chips,
            seat_index=None  # 离开后座位设为None
        )

        # WebSocket广播 - 向房间广播玩家离开事件
        # await broadcast_to_room(room_id, "player_leave", {
        #     "user_id": user_id,
        #     "username": user.username,
        #     "seat_index": seat_index,
        #     "returned_chips": total_return_chips,
        #     "action": "leave",
        #     "message": f"玩家 {user.username} 离开了房间",
        #     "leave_type": "leave"  # 区分离开类型：leave(简单离开) vs exit(正常退出)
        # })
        # WebSocket广播 - 向房间广播玩家退出事件
        await broadcast_to_room(room_id, "player_leave", {
            "user_id": user_id,
            "username": user.username,
            "seat_index": seat_index,
            "returned_chips": left_chips,
            "action": "exit",
            "message": f"玩家 {user.username} 退出了房间，带走了 {left_chips} 筹码",
            "leave_type": "exit"  # 区分退出类型：exit(正常退出) vs leave(简单离开)
        })
        logger.info(f"玩家 {user.username} 退出了房间，带走了 {left_chips} 筹码")

        return 200, "离桌成功喵～"
    
    except Exception as e:
        return 500, f"离开房间失败: {str(e)}"






